/*
 *
 *  *    Copyright 2020-2021 Luter.me
 *  *
 *  *    Licensed under the Apache License, Version 2.0 (the "License");
 *  *    you may not use this file except in compliance with the License.
 *  *    You may obtain a copy of the License at
 *  *
 *  *      http://www.apache.org/licenses/LICENSE-2.0
 *  *
 *  *    Unless required by applicable law or agreed to in writing, software
 *  *    distributed under the License is distributed on an "AS IS" BASIS,
 *  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  *    See the License for the specific language governing permissions and
 *  *    limitations under the License.
 *
 */

package com.luter.heimdall.plugins.redis.limiter;

import com.luter.heimdall.core.config.ConfigManager;
import com.luter.heimdall.core.config.HeimdallProperties;
import com.luter.heimdall.core.exception.HeimdallCacheException;
import com.luter.heimdall.core.limiter.PasswordRetryLimiter;
import com.luter.heimdall.core.utils.StrUtils;
import lombok.Data;
import lombok.experimental.Accessors;
import org.slf4j.Logger;
import org.springframework.data.redis.core.StringRedisTemplate;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import static org.slf4j.LoggerFactory.getLogger;

/**
 * 基于Redis缓存 登录重试次数限制器
 *
 * @author Luter
 */
@Data
@Accessors(chain = true, fluent = true)
public class RedisPasswordRetryLimiter implements PasswordRetryLimiter {

    /**
     * The constant log.
     */
    private static final transient Logger log = getLogger(RedisPasswordRetryLimiter.class);
    /**
     * The Retry cache.
     */
    private StringRedisTemplate retryCache;

    /**
     * Instantiates a new Cached authentication retry limit.
     *
     * @param retryCache the retry cache
     */
    public RedisPasswordRetryLimiter(StringRedisTemplate retryCache) {
        if (null == retryCache) {
            throw new HeimdallCacheException("String RedisTemplate can not be null ");
        }
        this.retryCache = retryCache;
    }

    @Override
    public boolean overLimitWhenIncremented(String key) {
        HeimdallProperties config = ConfigManager.getConfig();
        final int attempts = config.getLimiter().getAttempts();
        final long lockedDuration = config.getLimiter().getLockedDuration();
        log.debug("[overLimitWhenIncremented]::key = [{}]", key);
        String cacheKey = config.getLimiter().getCachePrefix() + key;
        log.debug("[overLimitWhenIncremented]::cacheKey = [{}]", cacheKey);
        if (config.getLimiter().isEnabled()) {
            log.info("[overLimitWhenIncremented]:: The Retry Limiter is enabled, key = [{}],cacheKey = [{}]", key, cacheKey);
            //先拿
            final String s = retryCache.opsForValue().get(cacheKey);
            if (StrUtils.isBlank(s)) {
                log.debug("[overLimitWhenIncremented]:: First retry ,Initialize and set to 0, key = [{}],cacheKey = [{}]", key, cacheKey);
                retryCache.opsForValue().set(cacheKey, 1 + "", lockedDuration, TimeUnit.MINUTES);
            } else {
                AtomicInteger retryCount = new AtomicInteger(Integer.parseInt(s));
                log.info("[overLimitWhenIncremented]::  key = [{}],cacheKey = [{}],retry times = [{}] ", key, cacheKey, retryCount.get());
                if (retryCount.incrementAndGet() > attempts) {
                    log.error("[overLimitWhenIncremented]:: CacheKey: [{}] retry times: [{}], " +
                            "exceeding the maximum number limit: [{}], lock the account", cacheKey, retryCount, attempts);
                    return true;
                }
                //没超，写入缓存
                retryCache.opsForValue().set(cacheKey, retryCount.get() + "", lockedDuration, TimeUnit.MINUTES);
            }
        } else {
            log.debug("[increase]::  The Retry Limiter is Disabled.  key = [{}]", key);
        }

        return false;
    }

    @Override
    public void release(String key) {
        HeimdallProperties config = ConfigManager.getConfig();
        retryCache.delete(config.getLimiter().getCachePrefix() + key);
    }

    @Override
    public int count(String key) {
        HeimdallProperties config = ConfigManager.getConfig();
        final String s = retryCache.opsForValue().get(config.getLimiter().getCachePrefix() + key);
        if (null != s) {
            return Integer.parseInt(s);
        }
        return 0;
    }

    @Override
    public int availableTimes(String key) {
        HeimdallProperties config = ConfigManager.getConfig();
        return config.getLimiter().getAttempts() - count(key);
    }
}
